package com.xunk.equipment.service;

import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.google.common.collect.Lists;
import com.xunk.common.core.constant.CacheConstants;
import com.xunk.common.core.constant.HttpStatus;
import com.xunk.common.core.domain.EquipmentInfoVO;
import com.xunk.common.core.domain.EquipmentResponse;
import com.xunk.common.core.enums.DeviceEnum;
import com.xunk.common.core.utils.*;
import com.xunk.common.redis.service.RedisService;
import com.xunk.common.security.utils.SecurityUtils;
import com.xunk.equipment.api.domain.MessageNotificationDO;
import com.xunk.equipment.domain.EquipmentBaseInfoDO;
import com.xunk.equipment.domain.EquipmentDO;
import com.xunk.equipment.domain.HomeDO;
import com.xunk.equipment.mapper.EquipmentMapper;
import com.xunk.equipment.mapper.HomeMapper;
import com.xunk.equipment.mapper.MessageMapper;
import com.xunk.equipment.vo.response.MessageNotificationResponse;
import com.xunk.equipment.vo.response.RoomInfoVO;
import com.xunk.equipment.vo.response.Weather;
import com.xunk.equipment.websocket.WebsocketManager;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @ProjectName: xunk
 * @Package: com.xunk.equipment.service
 * @ClassName: BaseServiceImpl
 * @Author: yangwendong
 * @Description:
 * @Date: 2021/3/30 9:58
 * @Version: 1.0
 */
@Service
@Slf4j
public class BaseService {
    @Autowired
    RedisService redisService;

    @Autowired
    HomeMapper homeMapper;

    @Autowired
    EquipmentMapper equipmentMapper;

    @Autowired
    MessageMapper messageMapper;

    /**
     * 获取设备基础信息-DO
     *
     * @param deviceSn
     * @return
     */
    public EquipmentDO getEquipmentInfo(String deviceSn) {
        return getEquipmentInfo(deviceSn, true);
    }

    public EquipmentDO getEquipmentInfo(String deviceSn, boolean isAssert) {
        XunkAssert.notEmpty(deviceSn, "设备号为空");
        EquipmentDO equipmentDO = redisService.getCacheObject(CacheConstants.EQUIPMENT_INFO_KEY + deviceSn);
        if (DataUtils.isNotEmpty(equipmentDO)) {
            return equipmentDO;
        }
        EquipmentDO equipment = equipmentMapper.getEquipment(deviceSn);
        if (isAssert) {
            XunkAssert.notEmpty(equipment, "设备号:" + deviceSn + " 不存在");
        }
        if (DataUtils.isNotEmpty(equipment)) {
            redisService.setCacheObject(CacheConstants.EQUIPMENT_INFO_KEY + deviceSn, equipment, 30, TimeUnit.MINUTES);
        }
        return equipment;
    }

    /**
     * 根据ownId查询deptId
     *
     * @param ownId
     * @return
     */
    public Long findDeptIdByOwnId(String ownId) {
        String key = CacheConstants.FIND_DEPTID_BY_OWNID + ownId;
        Long deptId = redisService.getCacheObject(key);
        if (DataUtils.isEmpty(deptId)) {
            deptId = homeMapper.findDeptIdByOwnId(ownId);
            XunkAssert.notEmpty(deptId, "ownId = " + ownId + " 未查询到对应的deptId");
            redisService.setCacheObject(key, deptId);
        }
        return deptId;
    }

    /**
     * 根据deptId查询ownId
     *
     * @param deptId
     * @return
     */
    public String findOwnIdByDeptId(Long deptId) {
        String key = CacheConstants.FIND_OWNID_BY_DEPTID + deptId;
        String ownId = redisService.getCacheObject(key);
        if (DataUtils.isEmpty(ownId)) {
            ownId = homeMapper.findOwnIdByDeptId(deptId);
            XunkAssert.notEmpty(deptId, "deptId = " + deptId + " 未查询到对应的ownId");
            redisService.setCacheObject(key, ownId);
        }
        return ownId;
    }

    /**
     * 根据deptId 拼接设备查询时需要的key= 项目id:区域id：社区id 或者 项目id：区域id：社区id：deptId
     * 这个key在各业务里需要继续拼接设备id，即redis中的key = 当前返回的key：设备deviceSn
     *
     * @param deptId
     * @return
     */
    public String findAncestorByDeptId(Long deptId) {
        String ancestors = homeMapper.findAncestorByDeptId(deptId);
        XunkAssert.notEmpty(ancestors, "deptId = " + deptId + " 未查询到祖籍目录");
        return String.join(":", ancestors.replaceFirst("0,", "").concat(",").concat(String.valueOf(deptId)).split(","));
    }


    public List<EquipmentInfoVO> equipmentList(Long homeId, Integer deviceSource) {
        HomeDO home = validHomeInfo(homeId);

        // 获取对应的设备列表
        List<EquipmentDO> equipmentDOS = equipmentMapper.listEquipments(home.getDeptId(), deviceSource);
        String description = home.getDescription();
        Map<String, String> roomDesc = Optional.ofNullable(description).map(s -> JSON.parseObject(s, new TypeReference<Map<String, String>>() {
        })).orElseGet(HashMap::new);
        Long userId = SecurityUtils.getUserId();
        return equipmentDOS.parallelStream().map(item -> {
//            if (Objects.equals(DeviceEnum.parseDeviceEnum(item.getDeviceType()), DeviceEnum.GATEWAY)) {
//                return null;
//            }
            IEquipmentService serviceHandler = getServiceHandler(item.getSupplier(), item.getDeviceType());
            EquipmentInfoVO vo = new EquipmentInfoVO();
            vo.setDeviceName(item.getDeviceNickname());
            vo.setDeviceSn(item.getDeviceSn());
            vo.setSupplier(item.getSupplier());
            vo.setDeviceType(item.getDeviceType());
            vo.setOnline(Objects.equals(item.getDeviceOnline(), 1L));
            vo.setRoomId(item.getRoomNum());
            vo.setRoomName(Optional.ofNullable(item.getRoomNum()).map(roomDesc::get).orElse(""));
            vo.setDeviceSource(item.getDeviceSource());
            vo.setHomeId(homeId);
            try {
                ThreadLocalUtils.put("userId", userId);
                EquipmentResponse equipmentInfo = serviceHandler.getEquipmentInfo(item);
                if (DataUtils.isNotEmpty(equipmentInfo) && DataUtils.isNotEmpty(equipmentInfo.getOnline())) {
                    vo.setOnline(equipmentInfo.getOnline());
                }
                vo.setInfo(equipmentInfo);
            } catch (Exception e) {
                log.error("获取设备基本信息异常 {}", item.getDeviceSn(), e);
            } finally {
                ThreadLocalUtils.clear();
            }
            return vo;
        }).filter(Objects::nonNull).collect(Collectors.toList());
    }

    private HomeDO validHomeInfo(Long homeId) {
        // 根据homeId查询对应的deptId
        HomeDO home = homeMapper.getHomeById(homeId);

        Long userId = SecurityUtils.getUserId();
        // 根据userId查询当前用户的家和将要查询的是否对应
        List<Long> longs = homeMapper.listHomeIdsByUserId(userId);
        XunkAssert.isTrue(longs.contains(homeId), "不符合的家庭信息");
        return home;
    }

    @Autowired
    ApplicationContext applicationContext;

    private IEquipmentService getServiceHandler(String supplier, String deviceType) {
        return applicationContext.getBean(supplier.concat("-").concat(deviceType), IEquipmentService.class);
    }

    public List<RoomInfoVO> roomList(Long homeId) {
        // 根据homeId查询家庭信息
        HomeDO home = validHomeInfo(homeId);
        String description = home.getDescription();
        if (DataUtils.isNotEmpty(description)) {
            Map<String, String> jsonObject = JSON.parseObject(description, new TypeReference<HashMap<String, String>>() {
            });
            return jsonObject.entrySet().stream().map(entry -> {
                RoomInfoVO vo = new RoomInfoVO();
                vo.setRoomId(entry.getKey());
                vo.setRoomName(entry.getValue());
                return vo;
            }).collect(Collectors.toList());
        }
        return null;
    }


    public void changeDeviceName(String deviceSn, String name) {
        equipmentMapper.updateDeviceNickname(deviceSn, name);
    }

    /**
     * 保存消息推送数据
     *
     * @param messages
     */
    public void insertMessageNotifications(List<MessageNotificationDO> messages) {
        // 保存消息通知
//        messageMapper.insertMessageNotifications(messages);
        // 按照deptId和userId 找到对应的客户端发送消息
        messages.forEach(message -> {
            // 判断设备类型，门窗撤防下不发送
            if (ObjectUtil.isNotEmpty(message.getTriggerId())) {
                EquipmentDO equipmentInfo = getEquipmentInfo(message.getTriggerId());
                if (ObjectUtil.isNotEmpty(equipmentInfo) && Objects.equals(equipmentInfo.getDeviceType(), DeviceEnum.DOOR_WINDOW_SENSOR.getDeviceType())) {
                    Boolean armingStateByDeviceSn = equipmentMapper.getArmingStateByDeviceSn(message.getTriggerId());
                    if (!armingStateByDeviceSn) {
                        // 如果撤防，则不发送
                        return;
                    }
                }
            }
            messageMapper.insertMessageNotifications(Lists.newArrayList(message));
            Map<String, Object> m = new HashMap<>();
            m.put("code", HttpStatus.SUCCESS);
            m.put("msg", "消息通知");
            m.put("type", message.getType());

            Map<String, Object> data = new HashMap<>();
            data.put("time", message.getCreateTime());
            data.put("content", message.getContent());
            data.put("id", message.getId());
            data.put("isRead", 0);
            data.put("title", message.getTitle());
            m.put("data", data);
            if (Objects.nonNull(message.getUserId())) {
                // 用户id不为空，则针对用户发送
                WebsocketManager.appointSending(String.valueOf(message.getUserId()), JSON.toJSONString(m));
            } else {
                // 根据deptId查询当前家庭下所有的用户发送
                List<Long> userIds = homeMapper.listUserIdsByDeptId(message.getDeptId());
                userIds.forEach(userId -> WebsocketManager.appointSending(String.valueOf(userId), JSON.toJSONString(m)));
            }
        });
    }

    public List<MessageNotificationResponse> listMessageNotifications(Long userId, Integer type) {
        List<MessageNotificationDO> messageNotificationDOS = messageMapper.listMessageNotifications(userId, type);
        return messageNotificationDOS.stream().map(item -> {
            MessageNotificationResponse response = new MessageNotificationResponse();
            response.setId(item.getId());
            response.setContent(item.getContent());
            response.setIsRead(item.getIsRead());
            response.setCreateTime(item.getCreateTime().getTime() / 1000);
            response.setType(item.getType());
            response.setTitle(item.getTitle());
            return response;
        }).collect(Collectors.toList());
    }

    /**
     * 获取消息未读条数
     *
     * @param userId
     * @param type
     * @return
     */
    public Integer countUnReadMessage(Long userId, Integer type) {
        return messageMapper.countUnreadMessage(userId, type);
    }

    public Boolean readMessageNotification(Long id, Integer type) {
        if (DataUtils.isEmpty(id)) {
            return false;
        }
        return messageMapper.readMessageNotification(SecurityUtils.getUserId(), Objects.equals(id, -1L) ? null : id, type) > 0;
    }

    public EquipmentBaseInfoDO getEquipmentBaseInfo(Long id) {
        if (DataUtils.isEmpty(id)) {
            return null;
        }
        return equipmentMapper.getDeviceBaseInfoById(id);
    }

    public static String MODEL_KEY = "hainayun:model:";

    public Map<String, Object> getCandidateModel(Long id) {
        if (DataUtils.isEmpty(id)) {
            return new HashMap<>();
        }
        String key = MODEL_KEY + id;
        Map<String, Object> cacheObject = redisService.getCacheObject(key);
        if (Objects.nonNull(cacheObject)) {
            return cacheObject;
        }
        EquipmentBaseInfoDO equipmentBaseInfo = getEquipmentBaseInfo(id);
        Map<String, Object> cache;
        if (DataUtils.isNotEmpty(equipmentBaseInfo) && DataUtils.isNotEmpty(equipmentBaseInfo.getCandidateModel())) {
            cache = JSON.parseObject(equipmentBaseInfo.getCandidateModel(), new TypeReference<Map<String, Object>>() {
            });
        } else {
            cache = new HashMap<>();
        }
        redisService.setCacheObject(key, cache, 12, TimeUnit.HOURS);
        return cache;
    }

    public Weather getWeatherInfo(Long addressCode) {
        Weather weather = redisService.getCacheObject("weather:" + addressCode);

        if (BeanUtil.isEmptyBean(weather)) {
            //调用高德API获取天气
            JSONObject response = WeatherUtil.queryWeatherFromAmap(addressCode);

            weather = JSON.toJavaObject(response, Weather.class);
            String redisKey = "weather:" + weather.getAdcode();
            //将该地区天气存入缓存设置超时时间3600s
            redisService.setCacheObject(redisKey, weather, 3600, TimeUnit.SECONDS);
            log.info("地区，{} 天气缓存成功：{} ", addressCode, JSON.toJSONString(weather));
        }
        return weather;
    }
}
